//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2006
//
// Move point of polygon/polyline.
//

# include "StdAfx.h"
# include "Resource.h"
# include "MovePoint.h"
# include "UndoRedo.h"
# include "CombinedAction.h"
# include "RGNodes.h"
# include "Label.h"
# include "MapView.h"
# include "Globals.h"

class CMovePointAction : public CEditAction {
  public:
	CMovePointAction (rgn_t * _pRgn, size_t _cElement, size_t _cPoint, const point_t & _pointNew) :
		m_pRgn (_pRgn),
		m_cElement (_cElement),
		m_cPoint (_cPoint),
		m_pointOld (_pRgn->elements [_cElement].points [_cPoint]),
		m_pointNew (_pointNew),
		m_bWasModified ((_pRgn->dwFlags & rgn_t::fModified) != 0)
	{}

	virtual void GetName (string_t & _str) const;
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t * const m_pRgn;
	size_t  const m_cElement;
	size_t  const m_cPoint;
	point_t const m_pointNew;
	point_t const m_pointOld;
	bool    const m_bWasModified;
};

void CMovePointAction::GetName (string_t & _str) const {
	_str.append ("move node [");
	GetHintText (* m_pRgn, _str);
	_str.append ("]");
}

bool CMovePointAction::Do () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_cElement < m_pRgn->elements.size ());
	rgn_element_t & element = m_pRgn->elements [m_cElement];

	element.points [m_cPoint] = m_pointNew;

	m_pRgn->RefreshAttributes (g_map);
	m_pRgn->dwFlags |= rgn_t::fModified;

	return true;
}

void CMovePointAction::Undo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_cElement < m_pRgn->elements.size ());
	rgn_element_t & element = m_pRgn->elements [m_cElement];

	element.points [m_cPoint] = m_pointOld;

	m_pRgn->RefreshAttributes (g_map);
	if (! m_bWasModified)
		m_pRgn->dwFlags &= ~rgn_t::fModified;
}

void CMovePointAction::Redo () {
	CMovePointAction::Do ();
}

///////////////////////////////////////

void MovePoint (rgn_t * _pRgn, size_t _cElement, size_t _cPoint, const point_t & _new) {
	assert (_pRgn);
	assert (_cElement < _pRgn->elements.size ());
	assert (_cPoint < _pRgn->elements [_cElement].points.size ());

	if (_cElement >= _pRgn->elements.size ())
		return;
	const rgn_element_t & element0 = _pRgn->elements [_cElement];

	rgnode_t * const pNode0 = GetRGNode (* _pRgn, _cElement, _cPoint);
	if (pNode0) {
		//
		// Move all the connected nodes.
		//
		const int iDepth = BeginCombinedAction ();

		rgns_t & polylines = g_map.layers [rkPolyline].rgns;
		for (rgns_t::iterator iRgn = polylines.begin (); iRgn != polylines.end (); ++ iRgn) {
			rgn_t & rgn = * iRgn;
			if (rgn.IsDeleted () || rgn.IsWTR ())
				continue;

			const size_t cElements = rgn.elements.size ();
			for (size_t cElement = 0; cElement < cElements; ++ cElement) {
				rgn_element_t & element = rgn.elements [cElement];
				if (element.bDeleted || element.cLevel != element0.cLevel)
					continue;

				const points_t & points = element.points;
				const size_t cPoints = points.size ();
				for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint) {
					rgnode_t * const pNode = GetRGNode (rgn, cElement, cPoint);
					if (pNode == pNode0) {
						PerformEditAction (new CMovePointAction (& rgn, cElement, cPoint, _new));
					}
				}
			}
		}

		EndCombinedAction (iDepth, GetStr (IDS_MOVE_NODES));
	} else {
		//
		// Move single node.
		//
		PerformEditAction (new CMovePointAction (_pRgn, _cElement, _cPoint, _new));
	}

	Refresh ();
}
